﻿using Jacobi.Vst.Core;
using Jacobi.Vst.Framework;
using Microsoft.VisualBasic;
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Linq;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Windows.Threading;
using System.Xml;
using Aleator.Core;
using Aleator.Core.Models;
using Aleator.Helpers;
using Aleator.Resources;
using System.Threading.Tasks;
using Sanford.Multimedia.Midi;

namespace Aleator.Core
{
    /// <summary>
    /// Session object generates MIDI data, organizes it and sends events to the VST host in syncronicity with the internal clock
    /// </summary>
    public class Session : INotifyPropertyChanged
    {
        #region Members
        private static PhraseFactory m_phraseFactory = new ConcretePhraseFactory();

        private Thread m_workerThread;
        private Aleator.Core.KeySignatureBuilder m_KeySignatureBuilder;

        private static NoteFactory m_notefactory = new ConcreteNoteFactory();

        private int m_iteration;
        private int m_keyNumber;

        private List<ConcreteNote> m_noteOnList;
        private List<ConcreteNote> m_noteOffList;

        private string m_suite;
        private bool m_isManual;

        private int m_selectedKeyValue;

        private int m_currentBeat;

        private int m_majorTicks;
        private int m_majorTicksPhrase;

        private int PPQN = Convert.ToInt16(AppResources.PulsesPerQuarterNoteString);

        private static int m_tempoMinimum = 70;
        private static int m_tempoCount = 30;

        public List<int> m_tempoList;
        private List<Track> m_tracks = new List<Track>();

        private int m_bpm;

        private int m_progressionCtr;

        private int m_oldProgressionID;

        private string m_oldChord;

        private int m_tempo;

        private bool m_metronome;
        private bool m_crash;

        private bool m_isPlaying;
        private bool m_drumsMuted;
        private bool m_bassMuted;
        private bool m_rhythmMuted;
        private bool m_melodyMuted;
        private bool m_arpMuted;
        private bool m_utilityMuted;

        private static bool m_paused;
        private static bool m_pending;

        private bool m_currentNoteIsPlaying;

        private ConcreteNote m_currentDisplayNote;

        private static MidiProcessor m_processor;
        private VstEventCollection m_events;
        private VstEventCollection m_eventsTmp;

        private TempoChangeBuilder m_tempoChanger;

        private List<Composition> m_alreadyPlayed;
        private List<CompositionLite> m_compositions;

        private Dictionary<int, string> m_keyBindingList;

        private List<ConcreteNote> m_notes;

        private CompositionLite m_selectedComposition;
        private Composition m_currentComposition;

        private XmlDocument m_document;
        private XmlNode m_root;
        private XmlNodeList  m_compositionList;

        private byte[] m_compositionIDs;

        private Microsoft.VisualBasic.Devices.ComputerInfo m_computerInfo;
        private DateTime m_startTime;

        private ConcretePhrase m_pendingPhrase;
        private PhraseFactory.PhraseType m_pendingPhraseType;

        private object m_lock;

        #endregion

        #region Constructors

        /// <summary>
        /// Generic constructor
        /// </summary>
        public Session(IVstMidiProcessor  midiProcessor, bool metronome)
        {
            Metronome = metronome;

            m_computerInfo = new Microsoft.VisualBasic.Devices.ComputerInfo();
            m_startTime = DateTime.MinValue;

            m_processor = (MidiProcessor)midiProcessor;
            m_events = new VstEventCollection();
            m_eventsTmp = new VstEventCollection();

            m_tempoList = new List<int>();

            for (int i = 70; i < 151; i++)
                m_tempoList.Add(i);

            //Tempo = 120;

            m_document = new XmlDocument();

            m_document.Load(Aleator.Resources.AppResources.CompositionDocumentPath);
            m_root = m_document.SelectSingleNode("/Compositions");
            m_compositionList = m_root.SelectNodes("Composition");
            m_compositionIDs = new byte[m_compositionList.Count];

            m_compositions = new List<CompositionLite>();
            m_alreadyPlayed = new List<Composition>();

            XmlNode c_node = null;

            for (int i = 0; i <= m_compositionList.Count; i++)
            {
                if (i < m_compositionList.Count)
                    m_compositionIDs[i] = (byte)(i + 1);

                if (i == 0)
                    m_compositions.Add(new CompositionLite(i, "Random selection", string.Empty));
                else
                {
                    c_node = m_compositionList.Item(i - 1);
                    m_compositions.Add(new CompositionLite(i, c_node.Attributes.GetNamedItem("Name").InnerText, c_node.Attributes.GetNamedItem("Mode").InnerText));
                }
            }

            if (SelectedComposition == null || SelectedComposition.CompositionID == 0)
                SelectedComposition = m_compositions[0];

            Suite = m_root.Attributes.GetNamedItem("Suite").InnerText;

            //Default MIDI note boundaries
            LowNote = 0;
            HighNote = 127;

            Clock = new MidiInternalClock();
            Clock.Ppqn = PPQN;

            m_tempoChanger = new TempoChangeBuilder();
            m_tempo = (int)m_processor.Tempo;

            //Stay within 3 octaves for now (HomeOctave - 1 to HomeOctave  1) for the whole session for now
            OctaveVariance = 1;

            DrumPhrase.LoadRhythms();

            LoadComposition(HighNote, LowNote);

            //m_workerThread = new Thread(() =>
            //{
            //    //LoadComposition(HighNote, LowNote);
            //    //TODO: Move this logic to the UI thread

            //    PrepareUIProperties();

            //    if (PropertyChanged != null)
            //    {
            //        SmartDispatcher.BeginInvoke(delegate
            //        {
            //            Loaded(this, null);
            //        });
            //    }
            //});

            //m_workerThread.IsBackground = true;
            //m_workerThread.Start();

            Logger.Log(TraceEventType.Information, "Session instantiated.");
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="highNote">Highest note that can be played during this session</param>
        /// <param name="lowNote">Lowest note that can be played durign this session</param>
        public Session(int highNote, int lowNote)
        {
            RndGenerator = new Random();
            Clock = new MidiInternalClock();

            //Set tempo equal to host tempo
            SetTempo();

            LoadComposition(highNote, lowNote);
        }

        #endregion

        #region Event Handlers
        /// <summary>
        /// Handler for the clock tick event. With the clock resolution (PPQN) set to 96, that means 48 ticks is the duration of an 8th and
        /// and 24 ticks is the duration of a 16th note. PPQN must be set higher for anything more granular than 32nd notes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Clock_Tick(object sender, EventArgs e)
        {
            if (CurrentComposition != null && (Clock.Ticks == 0 || Clock.Ticks % TickNumber == 0))
            {
                //Grab all of the notes for the current beat, measure, progression and composition counters
                if (m_computerInfo.AvailablePhysicalMemory < 4000000000)
                {
                    Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();

                    Logger.Log(TraceEventType.Error, "Reaper memory leak detected. Aleator memory allocation: " + currentProcess.WorkingSet64);
                    throw new OutOfMemoryException("Reaper memory leak detected. Aleator memory allocation: " + currentProcess.WorkingSet64);
                }

                GetEvents();

                //Iterate through all of the counters
                ProcessTicks();

                //Update UI with new measure and beat values
                NotifyUI();

                //Process and clear all MIDI events
                m_processor.Process(m_events);

                m_events.Clear();
            }
        }
        #endregion

        #region Events

        /// <summary>
        /// Event handler for the UI load
        /// </summary>
        public EventHandler Loaded;

        #endregion

        #region Properties

        [Bindable(true)]
        public List<CompositionLite> Compositions 
        {
            get
            {
                    return m_compositions.ToList();
            }
            set
            {
            }
        }

        /// <summary>
        /// Convenience property for suite
        /// </summary>
        public const string SuitePropertyName = "Suite";

        /// <summary>
        /// Name of the current suite
        /// </summary>
        [Bindable(true)]
        public string Suite
        {
            get
            {
                return m_suite;
            }
            set
            {
                m_suite = value;
            }
        }

        /// <summary>
        /// Title of the current movement
        /// </summary>
        public string Movement
        {
            get
            {
                return CurrentComposition.Name;
            }
        }

        /// <summary>
        /// Boolean indicating whether the phrases are being controlled manually
        /// </summary>
        public bool IsManual
        {
            get
            {
                return m_isManual;
            }
            set
            {
                m_isManual = value;
            }
        }

        /// <summary>
        /// Random number generator to be used for throughout session
        /// </summary>
        static public Random RndGenerator = new Random();

        /// <summary>
        /// MIDI clock to generate clicks
        /// </summary>
        static public MidiInternalClock Clock;

        /// <summary>
        /// Convenience property for CurrentBeat
        /// </summary>
        public const string CurrentBeatPropertyName = "CurrentBeat";

        /// <summary>
        /// Display beat + 1 to the user
        /// </summary>
        [Bindable(true)]
        public int CurrentBeat
        {
            get
            {
                return m_currentBeat + 1;
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Current measure of the session
        /// </summary>
        public int CurrentMeasure
        {
            get
            {
                return CurrentComposition.CurrentProgression.CurrentMeasure;
            }
        }

        /// <summary>
        /// Convenience property for CurrentMeasure
        /// </summary>
        public const string CurrentMeasurePropertyName = "CurrentMeasureDisplayNum";

        /// <summary>
        /// Display measure + 1 to the user 
        /// </summary>
        [Bindable(true)]
        public int CurrentMeasureDisplayNum
        {
            get
            {
                if (CurrentComposition != null)
                    return CurrentComposition.CurrentProgression.CurrentMeasure + 1;
                else
                    return 0;
            }
            set
            {
                //Do nothing - setter only here to enable UI binding
            }
        }

        /// <summary>
        /// LowNote noteID that can be played during this session
        /// </summary>
        public static int LowNote{ get; set; }

        /// <summary>
        /// HighNote noteID that can be played during this session
        /// </summary>
        public static int HighNote { get; set; }

        /// <summary>
        /// Practical resoulation for this session. PPQN divided by this number will determine ticknumber and therefore
        /// the minimum note length that can be processed. 1 means notes will be processed every 96 ticks (or every quarter note),
        /// 2 means every 48 (or eigth note), 4 means every 16th and 8 means every 32nd.
        /// </summary>
        public static int SessionResolution = Convert.ToInt16(AppResources.SessionResolutionString);

        /// <summary>
        /// Number of major ticks in a measure
        /// </summary>
        public int MajorTicksInMeasure
        {
            get
            {
                return CurrentComposition.CurrentProgression.Numerator * SessionResolution;
            }          
        }

        /// <summary>
        /// Determines the interval at which the tick handler will process events 
        /// </summary>
        public int TickNumber
        {
            get
            {
                return PPQN / SessionResolution;
            }
        }

        /// <summary>
        /// Number of total times in the phrase that events will be processed
        /// </summary>
        public int MajorTicksInPhrase
        {
            get
            {
                return MajorTicksInMeasure * CurrentComposition.CurrentProgression.TotalMeasures;
            }
        }

        /// <summary>
        /// Current tempo of this session in beats per minute
        /// </summary>
        [Bindable(true)]
        public int BPM 
        {
            get
            {
                return m_bpm;
            }
            set
            {
                m_bpm = value;
                NotifyPropertyChanged("BPM");
            }
        
        }

        /// <summary>
        /// The starting MIDI octave for this composition
        /// </summary>
        static public int? HomeOctave = 0;

        /// <summary>
        /// How many octaves the notes can go up or down
        /// </summary>
        static public int OctaveVariance;

        /// <summary>
        /// Use low octave for bass phrase
        /// </summary>
        static public int LowOctave
        {
            get
            {
                return HomeOctave.Value - OctaveVariance;
            }
        }

        /// <summary>
        /// Convenience property for SessionKey
        /// </summary>
        public const string CurrentKeyPropertyName = "CurrentKey";

        /// <summary>
        /// Current key of this session
        /// </summary>
        [Bindable(true)]
        public string CurrentKey
        {
            get
            {
                return CurrentComposition.CurrentProgression.Key.ToString();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Key sig selected by the user
        /// </summary>
        [Bindable(true)]
        public int SelectedKeyValue
        {
            get
            {
                return m_selectedKeyValue;
            }
            set
            {
                if (value > 0)
                {
                    m_KeySignatureBuilder.Key = (CircleOfFifths.Key)Enum.ToObject(typeof(CircleOfFifths.Key), value);
                    m_KeySignatureBuilder.Build();

                    CircleOfFifths.SetKey(m_KeySignatureBuilder.Key.ToString());
                }
                else
                    SetKey();

                if (value > 0 && value != m_selectedKeyValue)
                    LoadComposition(HighNote, LowNote);

                m_selectedKeyValue = (int)CircleOfFifths.CurrentKey;
         
                NotifyPropertyChanged("SelectedKeyValue");
                //NotifyPropertyChanged("Compositions");
            }
        }

        [Bindable(true)]
        public  Dictionary<int, string> Keys
        {
            get
            {
                Dictionary<int, string> keys = new Dictionary<int, string>();
                List<string> keystrings = new List<string>();

                if (m_keyBindingList == null)
                {
                    m_keyBindingList = new Dictionary<int, string>();

                    m_keyBindingList.Add(-1, "Random Selection");

                    foreach (CircleOfFifths.Key key in (CircleOfFifths.Key[])Enum.GetValues(typeof(CircleOfFifths.Key)))
                        m_keyBindingList.Add((int)key, key.ToString());
                }

                if (SelectedComposition.CompositionID == 0)
                    keys = m_keyBindingList;
                else
                {
                    if (m_selectedComposition.Mode == "Ionian")
                        keys = m_keyBindingList.Where(k => k.Value.Substring(k.Value.Length - 5, 5) == "Major"
                            || k.Key == -1).ToDictionary(d => d.Key, d => d.Value);
                    else
                        keys = m_keyBindingList.Where(k => k.Value.Substring(k.Value.Length - 5, 5) == "Minor"
                            || k.Key == -1).ToDictionary(d => d.Key, d => d.Value);
                }

                return keys;
            }
            set
            {
            }
        }

        /// <summary>
        /// Number of beats that comprise a measure in the current session
        /// </summary>
        public static int BeatsInMeasure;

        public static bool TransportOverride = true;

        /// <summary>
        /// Convenience property for TimeSignature
        /// </summary>
        public const string TimeSignaturePropertyName = "TimeSignature";

        /// <summary>
        /// String representation of the number of beats in the measure over the beat note value
        /// </summary>
        [Bindable(true)]
        public string TimeSignature
        {
            get
            {
                if (CurrentComposition != null)
                    return String.Format("{0}/{1}", CurrentComposition.CurrentProgression.Numerator, Composition.Denominator);
                else
                    return string.Empty;
            }
            set
            {
                //Do nothing for now
            }
        }

        /// <summary>
        /// Convenience property for ScaleDegrees
        /// </summary>
        public const string ScaleDegreesPropertyName = "ScaleDegrees";

        /// <summary>
        /// Scale degrees of the current progression
        /// </summary>
        [Bindable(true)]
        public string ScaleDegrees
        {
            get
            {
                if (CurrentComposition == null)
                    return string.Empty;
                else
                    return CurrentComposition.CurrentProgression.ScaleDegrees;
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for PhraseLength
        /// </summary>
        public const string PhraseLengthPropertyName = "PhraseLength";

        /// <summary>
        /// Total length in measures of the current progression
        /// </summary>
        [Bindable(true)]
        public int PhraseLength
        {
            get
            {
                if (CurrentComposition == null)
                    return 0;
                else
                    return CurrentComposition.CurrentProgression.TotalMeasures;
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for MovementName
        /// </summary>
        public const string MovementNamePropertyName = "MovementName";

        /// <summary>
        /// Name of the current movement (not sure why this was necessary)
        /// </summary>
        [Bindable(true)]
        public string MovementName
        {
            get
            {
                if (CurrentComposition != null)
                    return CurrentComposition.Name;
                else
                    return string.Empty;
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for RhythmName
        /// </summary>
        public const string RhythmNamePropertyName = "RhythmName";

        /// <summary>
        /// Name of the current rhythm type
        /// </summary>
        [Bindable(true)]
        public string RhythmName
        {
            get
            {
                return CurrentComposition.CurrentProgression.DrumPhrase.PhraseRhythm.RhythmName;
            }
        }

        /// <summary>
        /// Convenience property for Tempo
        /// </summary>
        public const string TempoPropertyName = "Tempo";

        /// <summary>
        ///  Actual miliseconds per beat of the clock
        /// </summary>
        [Bindable(true)]
        public int Tempo
        {
            get
            {
                if (m_tempo != m_processor.Tempo)
                {
                    m_tempo = (int)m_processor.Tempo;

                    //Tempo is set in microseconds per beat so we need to calculate that
                    double tmpTempo = (Convert.ToDouble(60) / Tempo) * 1000000.0;

                    //Set the tempo of the internal clock
                    Clock.Tempo = (int)tmpTempo;
                }

                return m_tempo;
            }
            set
            {
                if (m_tempo > value)
                {
                    int count = m_tempo - value;

                    for (int i = 0; i < count; i++)
                    {
                        m_events.Add(new VstMidiEvent(0, 0, 0, ConcreteNote.TempoDownEvent.Data, 0, 0));
                        m_processor.Process(m_events);
                        m_events.Clear();

                        Thread.Sleep(100);
                    }
                }
                else if (m_tempo < value)
                {
                    int count = value - m_tempo;

                    for (int i = 0; i < count; i++)
                    {
                        m_events.Add(new VstMidiEvent(0, 0, 0, ConcreteNote.TempoUpEvent.Data, 0, 0));
                        m_processor.Process(m_events);
                        m_events.Clear();

                        Thread.Sleep(100);
                    }
                }

                m_tempo = value;
            }
        }

        [Bindable(true)]
        public List<int> TempoList
        {
            get
            {
                return m_tempoList;
            }
            set
            {
            }
        }

        /// <summary>
        /// Indicates whether or not metronome is turned on
        /// </summary>
        [Bindable(true)]
        public bool Metronome
        {
            get
            {
                return m_metronome;
            }
            set
            {
                m_metronome = value;
                NotifyPropertyChanged("Metronome");
            }
        }

        /// <summary>
        /// Indicates whether or not crash is enabled
        /// </summary>
        [Bindable(true)]
        public bool Crash
        {
            get
            {
                return m_crash;
            }
            set
            {
                m_crash = value;
                NotifyPropertyChanged("Crash");
            }
        }

        public static bool Paused
        {
            get
            {
                return m_paused;
            }
        }

        /// <summary>
        /// Handler for UI binding
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Handler for static properties
        /// </summary>
        public static event PropertyChangedEventHandler StaticPropertyChanged;

        //MIDI/VST Properties

        /// <summary>
        /// List of all notes of the current progression
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> Notes
        {
            get
            {
                return m_notes;
            }
            set
            {
                //Do nothing
            }
        }
        
        /// <summary>
        /// Convenience property for DrumNotes
        /// </summary>
        public const string DrumNotesPropertyName = "DrumNotes";

        /// <summary>
        /// List of drum notes in the currently playing progression
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> DrumNotes 
        {
            get
            {
                return Notes.Where(n => n.Type == ConcreteNote.NoteType.Drum).ToList();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for BassNotes
        /// </summary>
        public const string BassNotesPropertyName = "BassNotes";

        /// <summary>
        /// List of bass notes in the currently playing progression
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> BassNotes 
        {
            get
            {
                return Notes.Where(n => n.Type == ConcreteNote.NoteType.Bass).ToList();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for LeadNotes
        /// </summary>
        public const string LeadNotesPropertyName = "LeadNotes";

        /// <summary>
        /// Notes that comprise the current lead melody
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> LeadNotes
        {
            get
            {
                return Notes.Where(n => n.Type == ConcreteNote.NoteType.Lead).ToList();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for RhythmNotes
        /// </summary>
        public const string RhythmNotesPropertyName = "RhythmNotes";

        /// <summary>
        /// Notes that comprise current rhythm accompanyment
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> RhythmNotes
        {
            get
            {
                return Notes.Where(n => n.Type == ConcreteNote.NoteType.Rhythm).ToList();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for ArpNotes
        /// </summary>
        public const string ArpNotesPropertyName = "ArpNotes";

        /// <summary>
        /// Notes being sent to the arpeggiator
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> ArpNotes
        {
            get
            {
                return Notes.Where(n => n.Type == ConcreteNote.NoteType.Arpeggio).ToList();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for UtilityNotes
        /// </summary>
        public const string UtilityNotesPropertyName = "UtilityNotes";

        /// <summary>
        /// Utility notes (crashes/toms, samples, noises)
        /// </summary>
        [Bindable(true)]
        public List<ConcreteNote> UtilityNotes
        {
            get
            {
                return Notes.Where(n => n.Type == ConcreteNote.NoteType.Utility).ToList();
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Convenience property for CurrentChord
        /// </summary>
        public const string CurrentChordPropertyName = "CurrentChord";

        /// <summary>
        /// The chord that is currently being played
        /// </summary>
        [Bindable(true)]
        public string CurrentChord 
        {
            get
            {
                //Chords only change on quarter notes for now
                if (CurrentComposition == null)
                    return string.Empty;
                else
                {
                    string chord = CurrentComposition.CurrentProgression.Chords.Where(c => c.Key <= (m_currentBeat
                        + (CurrentComposition.Numerator * CurrentMeasure))).OrderByDescending(c => c.Key).First().Value.DisplayName;

                    if (chord != m_oldChord)
                        m_oldChord = chord;

                    return m_oldChord;
                }
            }
            set
            {
                //Do nothing
            }
        }

        /// <summary>
        /// Composition currently being played
        /// </summary>
        public Composition CurrentComposition
        {
            get
            {
                return m_currentComposition;
            }
            private set
            {
                m_currentComposition = value;
            }
        }

        /// <summary>
        /// Convenience property for CurrentDisplayNote
        /// </summary>
        public const string CurrentDisplayNotePropertyName = "CurrentDisplayNote";

        public  CompositionLite SelectedComposition
        {
            get
            {
                return m_selectedComposition;
            }
            set
            {
                m_selectedComposition = value;

                if (!m_isPlaying && m_selectedComposition.CompositionID > 0)
                {
                    LoadComposition(HighNote, LowNote);
                }

                NotifyPropertyChanged(MovementNamePropertyName);
                NotifyPropertyChanged(CurrentKeyPropertyName);

                NotifyPropertyChanged(PhraseLengthPropertyName);
                NotifyPropertyChanged(TimeSignaturePropertyName);
                NotifyPropertyChanged(ScaleDegreesPropertyName);

                NotifyPropertyChanged("IsKeySelectable");
                NotifyPropertyChanged("SelectedComposition");
                NotifyPropertyChanged("Keys");
            }
        }

        /// <summary>
        /// Note currently selected by user
        /// </summary>
        [Bindable(true)]
        public ConcreteNote CurrentDisplayNote 
        {
            get
            {
                return m_currentDisplayNote;
            }
            set
            {
                try
                {
                    m_currentDisplayNote = value;
                    NotifyPropertyChanged(CurrentDisplayNotePropertyName);
                }
                catch (Exception)
                {
                }
            }
        }

        /// <summary>
        /// Convenience property for DrumsMuted
        /// </summary>
        public const string DrumsMutedPropertyName = "DrumsMuted";

        /// <summary>
        /// Drum mute property
        /// </summary>
        [Bindable(true)]
        public bool DrumsMuted 
        {
            get
            {
                return m_drumsMuted;
            }
            set
            {
                m_drumsMuted = value;
                NotifyPropertyChanged(DrumsMutedPropertyName);
            }
        }

        /// <summary>
        /// Convenience property for DrumsMuted
        /// </summary>
        public const string BassMutedPropertyName = "BassMuted";

        /// <summary>
        /// Drum mute property
        /// </summary>
        [Bindable(true)]
        public bool BassMuted
        {
            get
            {
                return m_bassMuted;
            }
            set
            {
                m_bassMuted = value;
                NotifyPropertyChanged(BassMutedPropertyName);
            }
        }

        /// <summary>
        /// Convenience property for DrumsMuted
        /// </summary>
        public const string RhythmMutedPropertyName = "RhythmMuted";

        /// <summary>
        /// Drum mute property
        /// </summary>
        [Bindable(true)]
        public bool RhythmMuted
        {
            get
            {
                return m_rhythmMuted;
            }
            set
            {
                m_rhythmMuted = value;
                NotifyPropertyChanged(RhythmMutedPropertyName);
            }
        }

        /// <summary>
        /// Convenience property for DrumsMuted
        /// </summary>
        public const string MelodyMutedPropertyName = "MelodyMuted";

        /// <summary>
        /// Drum mute property
        /// </summary>
        [Bindable(true)]
        public bool MelodyMuted
        {
            get
            {
                return m_melodyMuted;
            }
            set
            {
                m_melodyMuted = value;
                NotifyPropertyChanged(MelodyMutedPropertyName);
            }
        }

        /// <summary>
        /// Convenience property for DrumsMuted
        /// </summary>
        public const string ArpMutedPropertyName = "ArpMuted";

        /// <summary>
        /// Drum mute property
        /// </summary>
        [Bindable(true)]
        public bool ArpMuted
        {
            get
            {
                return m_arpMuted;
            }
            set
            {
                m_arpMuted = value;
                NotifyPropertyChanged(ArpMutedPropertyName);
            }
        }
        /// <summary>
        /// Convenience property for DrumsMuted
        /// </summary>
        public const string UtilityMutedPropertyName = "UtilityMuted";

        /// <summary>
        /// Drum mute property
        /// </summary>
        [Bindable(true)]
        public bool UtilityMuted
        {
            get
            {
                return m_utilityMuted;
            }
            set
            {
                m_utilityMuted = value;
                NotifyPropertyChanged(UtilityMutedPropertyName);
            }
        }

        /// <summary>
        /// Convenience property for CurrentNote
        /// </summary>
        public const string CurrentNoteIsPlayingPropertyName = "CurrentNoteIsPlaying";

        /// <summary>
        /// Used to notify the UI to turn the "is playing" indicator on/off for the currently playing note.
        /// </summary>
        public bool CurrentNoteIsPlaying
        {
            get
            {
                return m_currentNoteIsPlaying;
            }
            set
            {
                if (CurrentDisplayNote != null)
                {
                    System.Diagnostics.Debug.WriteLine(string.Format("Location: {0} | Measure {1}",
                        ((double)m_majorTicksPhrase % (double)MajorTicksInMeasure) / CurrentDisplayNote.MajorTickMultiplier,
                        CurrentMeasure));
                }

                m_currentNoteIsPlaying = value;
                NotifyPropertyChanged(CurrentNoteIsPlayingPropertyName);
            }
        }

        /// <summary>
        /// Boolean indiacator of if phrase is locked via UI
        /// </summary>
        public bool HoldPhrase;

        /// <summary>
        /// Number of times this session has run
        /// </summary>
        public int Iteration
        {
            get
            {
                return m_iteration;
            }
            set
            {
                m_iteration = value;
            }
        }

        [Bindable(true)]
        public bool IsKeySelectable
        {
            get
            {
                return m_selectedComposition.CompositionID > 0;
            }
            set
            {
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Looks through notes collection of the drum phrase and decides which MIDI events to add for this tick value
        /// Also adds events for metronaome and crash if they are enabled
        /// </summary>
        private void GetEvents()
        {
            m_noteOnList.Clear();
            m_noteOffList.Clear();

            #region Click Track / Crash
            //if (m_metronome)
            //{
            //    //Send metronome on message at the start of every measure and on every quarter note
            //    if (m_majorTicks == 0)
            //        m_events.Add(Note.MetronomeMeasureOnEvent);
            //    else if (m_majorTicks == SessionResolution || m_majorTicks % SessionResolution == SessionResolution)
            //        m_events.Add(Note.MetronomeQuarterOnEvent);

            //    //Send metronome off message to host every 8th note
            //    if (m_majorTicks == SessionResolution / 2 || m_majorTicks % SessionResolution == SessionResolution / 2)
            //        m_events.Add(Note.MetronomeOffEvent);
            //}

            ////Add crash to the end of the occasional measure (and turn it off) if the Crash boolean is set to true
            //if (m_crash)
            //{
            //    if (m_majorTicks == 0 && CurrentMeasure == 0 && RndGenerator.Next(2) == 1)
            //        m_events.Add(Note.CrashOnEvent);
            //    else if (m_majorTicks == SessionResolution / 2 && CurrentMeasure == 0)
            //        m_events.Add(Note.CrashOffEvent);
            //}
            #endregion

            var localnotes = CurrentComposition.CurrentProgression.Notes.ToList();

            //Add all on notes appropriate for the current measure/tick count
            m_noteOnList.AddRange(localnotes.Where(n => n.Location == 0 && n.Measure == 0  && n.Location == m_majorTicksPhrase));
            m_noteOnList.AddRange(localnotes.Where(n => CurrentMeasure == 0 && m_majorTicks > 0  && ((double)n.Location == (double)m_majorTicksPhrase / n.MajorTickMultiplier && n.Measure == CurrentMeasure)));
            m_noteOnList.AddRange(localnotes.Where(n => CurrentMeasure > 0 && ((double)n.Location == ((double)m_majorTicksPhrase % (double)MajorTicksInMeasure) / n.MajorTickMultiplier && n.Measure == CurrentMeasure)));

            //If the currently displaying note got added, indicate that is being played on the UI
            if (CurrentDisplayNote != null)
            {
                if (m_noteOnList.Where(n => CurrentDisplayNote.MessageOnID == n.MessageOnID && CurrentDisplayNote.NoteID == n.NoteID && n.Type != ConcreteNote.NoteType.Drum).Any())
                    CurrentNoteIsPlaying = true;
            }

            //Add all off notes appropriate for the current measure/tick count that we haven't added on notes for this cycle
            m_noteOffList.AddRange(localnotes.Where(n => CurrentMeasure == 0 && n.OffLocation == 0 && n.OffMeasure == 0 && n.OffLocation == m_majorTicks && n.Type != ConcreteNote.NoteType.Drum));
            m_noteOffList.AddRange(localnotes.Where(n => m_majorTicks > 0 && ((double)n.OffLocation == (double)m_majorTicksPhrase / n.MajorTickMultiplier && n.OffMeasure == CurrentMeasure && n.Type != ConcreteNote.NoteType.Drum)));
            m_noteOffList.AddRange(localnotes.Where(n => CurrentMeasure > 0 && ((double)n.OffLocation == ((double)m_majorTicksPhrase % (double)MajorTicksInMeasure) / n.MajorTickMultiplier && n.OffMeasure == CurrentMeasure && n.Type != ConcreteNote.NoteType.Drum)));

            //If the currently displaying note is being turned off, reflect it on the UI
            if (CurrentDisplayNote != null)
            {
                if (m_noteOffList.Where(n => CurrentDisplayNote.MessageOffID == n.MessageOffID && CurrentDisplayNote.NoteID == n.NoteID).Any())
                    CurrentNoteIsPlaying = false;
            }

            //Add of note events to processing list
            m_events.AddRange(m_noteOffList.Select(n => n.NoteOffEvent).AsEnumerable());

            //Add note on events to event list for processing where the note isn't being turned off
            m_events.AddRange(m_noteOnList.Where(n => !m_noteOffList.Contains(n)).Select(n => n.NoteOnEvent).AsEnumerable());
        }

        private void ProcessTicks()
        {
            //If we are on a beat change...
            if (m_majorTicks == SessionResolution - 1 || m_majorTicks % SessionResolution == SessionResolution - 1)
            {
                //If this is the end of a measure...
                if (m_majorTicks == MajorTicksInMeasure - 1)
                {
                    //If this is the last measure, set to 0. If not, iterate.
                    if (CurrentComposition.CurrentProgression.CurrentMeasure == CurrentComposition.CurrentProgression.TotalMeasures - 1)
                    {
                        //For now, kill the melody between progressions
                        m_events.AddRange(new VstEventCollection { ConcreteNote.MelodyOffEvent, ConcreteNote.BassOffEvent, ConcreteNote.RhythmOffEvent });

                        m_oldProgressionID = CurrentComposition.CurrentProgression.ProgressionID;

                        //If this is the last progression in the composition, go to the next one
                        if (m_progressionCtr == CurrentComposition.Progressions.Count - 1 && !HoldPhrase)
                        {
                            //Deselect key and composition
                            SelectedKeyValue = 0;
                            SelectedComposition = m_compositions[0];

                            string tracetext = String.Format("Completed movement: {0} | Elapsed Time: {1} seconds | Memory in use: {2} | Available memory: {3}",
                                CurrentComposition.Name,
                                (DateTime.Now - m_startTime),
                                System.Diagnostics.Process.GetCurrentProcess().WorkingSet64,
                                m_computerInfo.AvailablePhysicalMemory);

                            Logger.Log(TraceEventType.Information, tracetext);
                            Logger.Log(TraceEventType.Information, string.Format("Errors: {0} | EventErrors: {1}" + System.Environment.NewLine, AudioProcessor.Errors, AudioProcessor.EventErrors));

                            AudioProcessor.Errors = 0;
                            AudioProcessor.EventErrors = 0;

                            m_paused = true;
                            Clock.Stop();
                            m_events.Add(new VstMidiEvent(0, 0, 0, ConcreteNote.ScrollDownEvent.Data, 0, 0));
                            m_processor.Process(m_events);
                            SelectedComposition = m_compositions[0];
                            LoadComposition(HighNote, LowNote);
                            m_events.Clear();
                            Tempo = RndGenerator.Next(Int32.Parse(Resources.AppResources.MinTempo), Int32.Parse(Resources.AppResources.MaxTempo)) * 10;
                            Thread.Sleep(3000);
                            Clock.Continue();
                            m_paused = false;
                                
                            //Start at the first progression. Restart the tick counter as well.
                            m_progressionCtr = 0;
                            CurrentComposition.CurrentProgression = CurrentComposition.Progressions[m_progressionCtr];

                            NotifyPropertyChanged(MovementNamePropertyName);
                            NotifyPropertyChanged(CurrentKeyPropertyName);
                        }
                        //If not, go to the next progression
                        else if (!HoldPhrase)
                            CurrentComposition.CurrentProgression = CurrentComposition.Progressions[++m_progressionCtr];

                        //Either way this last measure of the progression so we have to restart the measure and phrase tick counters
                        m_majorTicksPhrase = 0;
                        CurrentComposition.CurrentProgression.CurrentMeasure = 0;

                        //Notify the UI of progression related changes if the ID has actually been updated
                        if (CurrentComposition.CurrentProgression.ProgressionID != m_oldProgressionID)
                        {
                            NotifyPropertyChanged(PhraseLengthPropertyName);
                            NotifyPropertyChanged(TimeSignaturePropertyName);
                            NotifyPropertyChanged(ScaleDegreesPropertyName);
                            NotifyPropertyChanged(CurrentKeyPropertyName);

                            ChangeNoteLists();
                        }
                    }
                    //This isn't the last measure so increment the measure and phrase tick counters
                    else
                    {
                        CurrentComposition.CurrentProgression.CurrentMeasure++;
                        m_majorTicksPhrase++;
                    }

                    //Either way, this is the end of a measure so restart beat counter
                    m_majorTicks = 0;
                    m_currentBeat = 0;
                }
                //This isn't the end of a measure so iterate beat, tick, and phrase tick counters
                else
                {
                    m_currentBeat++;
                    m_majorTicks++;
                    m_majorTicksPhrase++;
                }

                NotifyPropertyChanged(Session.CurrentChordPropertyName);
            }
            //If its not a beat change, it can't be the end of a phrase or measure so just iterate the ticks
            else
            {
                m_majorTicks++;
                m_majorTicksPhrase++;
            }
        }

        /// <summary>
        /// Sets tempo based on the host
        /// </summary>
        public void SetTempo()
        {  
            //Tempo is set in microseconds per beat so we need to calculate that
            double tmpTempo = (Convert.ToDouble(60) / Tempo) * 1000000.0;

            //Set the tempo of the internal clock
            Clock.Tempo = (int)tmpTempo;
        }

        /// <summary>
        /// Thise will select 1 of 30 possible key signatures for the session
        /// </summary>
        /// <returns></returns>
        public CircleOfFifths.Key SetKey(string mod = null)
        {
            //Set key sig for MIDI file
            m_KeySignatureBuilder = new KeySignatureBuilder();

            //No modulation was given so this is the first progression in the composition
            if (mod == null)
            {
                switch (CircleOfFifths.CurrentModeName)
                {
                    case CircleOfFifths.ModeName.Ionian:
                        m_keyNumber = RndGenerator.Next(12, 23);
                        break;
                    case CircleOfFifths.ModeName.Aeolian:
                        m_keyNumber = RndGenerator.Next(0, 11);
                        break;
                    case CircleOfFifths.ModeName.HarmonicMinor:
                        m_keyNumber = RndGenerator.Next(24, 35);
                        break;
                }
            }
            //Modulation from the previous key
            else
            {
                string[] mods = mod.Split(',');

                //We only work with basic modulation. The composer will need to supply a series of modulations for anything special.
                foreach (string s in mods)
                {
                    switch (s)
                    {
                        case "Subdominant":
                            if (m_keyNumber == 0 || m_keyNumber == 12 || m_keyNumber == 24)
                                m_keyNumber += 11;
                            else
                                m_keyNumber--;
                            break;
                        case "Dominant":
                            if (m_keyNumber == 11 || m_keyNumber == 23 || m_keyNumber == 25)
                                m_keyNumber -= 11;
                            else
                                m_keyNumber++;
                            break;
                        case "RelativeMajor":
                            m_keyNumber += 12;
                            break;
                        case "RelativeMinor":
                            m_keyNumber -= 12;
                            break;
                    }
                }
            }

            m_KeySignatureBuilder.Key = (CircleOfFifths.Key)Enum.ToObject(typeof(CircleOfFifths.Key), m_keyNumber);
            m_KeySignatureBuilder.Build();

            CircleOfFifths.SetKey(m_KeySignatureBuilder.Key.ToString());

            return m_KeySignatureBuilder.Key;
        }

        /// <summary>
        /// Load compositions
        /// </summary>
        public void LoadComposition(int highNote, int lowNote, bool oldComposition = false)
        {
            XmlNode nl;
            XmlNodeList progressionList;

            if (m_alreadyPlayed.Count == m_compositionIDs.Count())
                m_alreadyPlayed.Clear();

            byte[] playedints = m_alreadyPlayed.Where(c => c != null).Select(c => c.CompositionID).ToArray();
            byte[] eligible = m_compositionIDs.Where(c => !playedints.Contains(c)).ToArray();

            int compositionid = 0;

            if (SelectedComposition.CompositionID == 0)
            {
                if (!oldComposition)
                {
                    int id = eligible.Count();
                    compositionid = eligible[RndGenerator.Next((byte)id)];
                }
                else
                    compositionid = playedints[playedints.GetUpperBound(0)];

                //May have to move this outside of LoadComposition
                m_selectedComposition = m_compositions[compositionid];
                NotifyPropertyChanged("SelectedComposition");
            }
            else
                compositionid = SelectedComposition.CompositionID;

            nl = m_compositionList.Item(compositionid - 1);

            Composition composition = new Composition();

            HighNote = highNote;
            LowNote = lowNote;

            composition.CompositionID = Convert.ToByte(nl.Attributes.GetNamedItem("CompositionID").InnerText);
            composition.Name = nl.Attributes.GetNamedItem("Name").InnerText;
            composition.Mode = nl.Attributes.GetNamedItem("Mode").InnerText;

            bool modeswitch = CircleOfFifths.CurrentModeName.ToString() != composition.Mode;

            switch (composition.Mode)
            {
                case "Ionian":
                    CircleOfFifths.CurrentModeName = CircleOfFifths.ModeName.Ionian;
                    break;
                case "Aeolian":
                    CircleOfFifths.CurrentModeName = CircleOfFifths.ModeName.Aeolian;
                    break;
                case "Harmonic Minor":
                    CircleOfFifths.CurrentModeName = CircleOfFifths.ModeName.HarmonicMinor;
                    break;
            }

            //ONLY DO THIS IF NO KEY WAS SELECTED
            if (m_selectedKeyValue == 0 ||  modeswitch)
            {
                NotifyPropertyChanged("Keys");
                composition.KeySignature = SetKey();
                m_selectedKeyValue = (int)m_KeySignatureBuilder.Key;
            }
            else
                composition.KeySignature = m_KeySignatureBuilder.Key;

            CircleOfFifths.ModeName originalmodename = CircleOfFifths.CurrentModeName;

            NotifyPropertyChanged("SelectedKeyValue");

            if (nl.Attributes.GetNamedItem("Numerator") != null)
                composition.Numerator = Convert.ToByte(nl.Attributes.GetNamedItem("Numerator").InnerText);
            else
                composition.Numerator = 4;

            progressionList = nl.SelectNodes("Progression");

            foreach (XmlNode pl in progressionList)
            {
                byte b;
                string mod = null;

                if (pl.Attributes.GetNamedItem("Modulation") != null)
                    mod = pl.Attributes.GetNamedItem("Modulation").InnerText;

                if (pl != null)
                {
                    if (Byte.TryParse(pl.InnerText, out b))
                    {
                        //If modulation is populated, we need to change keys
                        if (!string.IsNullOrEmpty(mod))
                            composition.KeySignature = SetKey(mod);

                        composition.LoadProgression(pl);
                    }
                    else
                        throw new Exception("No ProgressionID found!");
                }
            }

            //Need to move back to the original mode of the composition here
            CircleOfFifths.CurrentModeName = originalmodename;

            CurrentComposition = composition;
            CurrentComposition.CurrentProgression = CurrentComposition.Progressions[0];
            CurrentComposition.CurrentProgression.CurrentMeasure = 0;

            ChangeNoteLists();

            CurrentDisplayNote = (ConcreteNote)m_notefactory.GetNote();

            if (SelectedComposition.CompositionID > 0 && !m_alreadyPlayed.Select(c => c.CompositionID).Contains(composition.CompositionID))
                m_alreadyPlayed.Add(composition);

            m_iteration++;
        }

        public void PrepareUIProperties()
        {
            //TODO: Move everything below (and associated logic) into the composition loop
            Crash = true;
            m_notes = new List<ConcreteNote>();

            m_noteOnList = new List<ConcreteNote>();
            m_noteOffList = new List<ConcreteNote>();

            NotifyPropertyChanged("SelectedKeyValue");
            NotifyPropertyChanged("SelectedComposition");
            NotifyPropertyChanged(SuitePropertyName);
            NotifyPropertyChanged(MovementNamePropertyName);
            NotifyUI();
        }

        /// <summary>
        /// Property changed handler coordinates with the dispatcher to make updates on the UI
        /// </summary>
        /// <param name="propertyName"></param>
        private void NotifyPropertyChanged(string propertyName)
        {
            try
            {
                if (PropertyChanged != null)
                {
                    SmartDispatcher.BeginInvoke(delegate
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    });
                }
            }
            catch (InvalidOperationException ioe)
            {
                Debug.WriteLine("Exception: " + ioe.Message);
            }
        }

        private static void StaticNotifyPropertyChanged(string propertyName)
        {
            try
            {
                if (StaticPropertyChanged != null)
                {
                    SmartDispatcher.BeginInvoke(delegate
                    {
                        StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
                    });
                }
            }
            catch (InvalidOperationException ioe)
            {
                Debug.WriteLine("Exception: " + ioe.Message);
            }
        }

        private void NotifyUI()
        {
            NotifyPropertyChanged(CurrentBeatPropertyName);
            NotifyPropertyChanged(CurrentMeasurePropertyName);
            NotifyPropertyChanged(TempoPropertyName);
        }

        private void ChangeNoteLists()
        {
            m_notes = null;
            m_notes = new List<ConcreteNote>();
            m_notes = CurrentComposition.CurrentProgression.Notes;

            CurrentDisplayNote = null;
            CurrentNoteIsPlaying = false;

            NotifyPropertyChanged(DrumNotesPropertyName);
            NotifyPropertyChanged(BassNotesPropertyName);
            NotifyPropertyChanged(RhythmNotesPropertyName);
            NotifyPropertyChanged(LeadNotesPropertyName);
            NotifyPropertyChanged(ArpNotesPropertyName);
            NotifyPropertyChanged(UtilityNotesPropertyName);

            NotifyPropertyChanged(CurrentDisplayNotePropertyName);
            NotifyPropertyChanged(CurrentNoteIsPlayingPropertyName);

            NotifyPropertyChanged(RhythmNamePropertyName);
        }

        #endregion

        #region UI (Live) Methods

        /// <summary>
        /// Start the MIDI clock
        /// </summary>
        public void Start()
        {
            Tempo = RndGenerator.Next(Int32.Parse(Resources.AppResources.MinTempo), Int32.Parse(Resources.AppResources.MaxTempo) + 1) * 10;

            m_isPlaying = true;
            m_paused = false;
            m_startTime = DateTime.Now;

            string tracetext = String.Format("Aleator clock starting. Start time: {0}", m_startTime.ToString());
            Logger.Log(TraceEventType.Information, tracetext);

            //Start the clock
            Clock.Tick += new EventHandler(Clock_Tick);
            Clock.Start();
        }

        /// <summary>
        /// Stop the MIDI clock
        /// </summary>
        public void Stop()
        {
            m_isPlaying = false;

            //TODO: Stop the worker thread.
            if (m_workerThread != null && m_workerThread.IsAlive)
            {
                try
                {
                    m_workerThread.Abort();
                    m_workerThread = null;
                }
                catch (ThreadAbortException tae)
                {
                    Debug.WriteLine(tae.Message);
                }
            }

            m_paused = true;
            m_startTime = DateTime.MinValue;

            string tracetext = String.Format("Aleator clock stopping. Current time: {0}", DateTime.Now.ToString());
            Logger.Log(TraceEventType.Information, tracetext);

            //Reset and stop the clock
            Clock.Tick -= Clock_Tick;
            Clock.Stop();
            Clock.SetTicks(0);

            m_events.Clear();

            m_events.AddRange(new VstEventCollection { ConcreteNote.MelodyOffEvent, ConcreteNote.BassOffEvent, ConcreteNote.RhythmOffEvent });

            m_processor.Process(m_events);
            m_events.Clear();

            //Notes.Clear();
            //CurrentComposition = null;

            CurrentComposition.CurrentProgression = CurrentComposition.Progressions[0];
            CurrentComposition.CurrentProgression.CurrentMeasure = 0;

            m_currentBeat = 0;
            m_majorTicks = 0;
            m_majorTicksPhrase = 0;

            CircleOfFifths.Chords = null;
        }

        /// <summary>
        /// Skip forward or back a composition
        /// </summary>
        /// <param name="move">Move forward or back a composition</param>
        public void ChangeComposition(int move)
        {
            m_events.Clear();

            if (move == 1)
                LoadComposition(HighNote, LowNote);
            else
                LoadComposition(HighNote, LowNote, true);

            CurrentComposition.CurrentProgression = CurrentComposition.Progressions[0];
            CurrentComposition.CurrentProgression.CurrentMeasure = 0;

            NotifyPropertyChanged(MovementNamePropertyName);
            NotifyPropertyChanged(CurrentKeyPropertyName);

            NotifyPropertyChanged(PhraseLengthPropertyName);
            NotifyPropertyChanged(TimeSignaturePropertyName);
            NotifyPropertyChanged(ScaleDegreesPropertyName);

            ChangeNoteLists();
        }

        /// <summary>
        /// Skio forward or back a progression
        /// </summary>
        /// <param name="move">Number to move forward or back</param>
        public void ChangeProgression(int move)
        {
            m_events.Clear();

            CurrentComposition.CurrentProgression = CurrentComposition.Progressions[m_progressionCtr + move];
            CurrentComposition.CurrentProgression.CurrentMeasure = 0;

            NotifyPropertyChanged(MovementNamePropertyName);
            NotifyPropertyChanged(CurrentKeyPropertyName);

            NotifyPropertyChanged(PhraseLengthPropertyName);
            NotifyPropertyChanged(TimeSignaturePropertyName);
            NotifyPropertyChanged(ScaleDegreesPropertyName);

            ChangeNoteLists();
        }

        /// <summary>
        /// Create a new phrase on the fly for this progression. This needs to be rewritten.
        /// </summary>
        /// <param name="phrasetype">Type of phrase to be retriggered</param>
        public void ReTrigger(PhraseFactory.PhraseType phrasetype)
        {
            m_pendingPhraseType = phrasetype;

            switch (phrasetype)
            {
                case PhraseFactory.PhraseType.Drum:
                    CurrentComposition.CurrentProgression.Notes.RemoveAll(n => n.Type == Note.NoteType.Drum);
                    CurrentComposition.CurrentProgression.DrumPhrase = (DrumPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Drum, CurrentComposition.CurrentProgression);
                    break;
                case PhraseFactory.PhraseType.Bass:
                    CurrentComposition.CurrentProgression.Notes.RemoveAll(n => n.Type == Note.NoteType.Bass);
                    CurrentComposition.CurrentProgression.BassPhrase = (BassPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Bass, CurrentComposition.CurrentProgression);
                    break;
                case PhraseFactory.PhraseType.Rhythm:
                    CurrentComposition.CurrentProgression.Notes.RemoveAll(n => n.Type == Note.NoteType.Rhythm);
                    CurrentComposition.CurrentProgression.RhythmPhrase = (RhythmPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Rhythm, CurrentComposition.CurrentProgression);
                    break;
                case PhraseFactory.PhraseType.Melody:
                    CurrentComposition.CurrentProgression.Notes.RemoveAll(n => n.Type == Note.NoteType.Lead);
                    //TODO: Figure out why its holding onto old melodies
                    CurrentComposition.CurrentProgression.Melodies.Clear();
                    CurrentComposition.CurrentProgression.LoadMelodies();
                    CurrentComposition.CurrentProgression.MelodyPhrase = (MelodyPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Melody, CurrentComposition.CurrentProgression);
                    break;
                default:
                    break;
            }

            SwapAll();
        }

        /// <summary>
        /// Mute the given phrasetype
        /// </summary>
        /// <param name="phrasetype">Kind of phrase to mute</param>
        public void Mute(PhraseFactory.PhraseType phrasetype)
        {
            switch (phrasetype)
            {
                case PhraseFactory.PhraseType.Drum:
                    CurrentComposition.CurrentProgression.DrumPhrase.Mute();
                    break;
                case PhraseFactory.PhraseType.Bass:
                    CurrentComposition.CurrentProgression.BassPhrase.Mute();
                    break;
                case PhraseFactory.PhraseType.Rhythm:
                    CurrentComposition.CurrentProgression.RhythmPhrase.Mute();
                    break;
                case PhraseFactory.PhraseType.Melody:
                    CurrentComposition.CurrentProgression.MelodyPhrase.Mute();
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// Mute the given phrasetype
        /// </summary>
        /// <param name="phrasetype">Kind of phrase to mute</param>
        public void UnMute(PhraseFactory.PhraseType phrasetype)
        {
            switch (phrasetype)
            {
                case PhraseFactory.PhraseType.Drum:
                    CurrentComposition.CurrentProgression.DrumPhrase.UnMute();
                    break;
                case PhraseFactory.PhraseType.Bass:
                    CurrentComposition.CurrentProgression.BassPhrase.UnMute();
                    break;
                case PhraseFactory.PhraseType.Rhythm:
                    CurrentComposition.CurrentProgression.RhythmPhrase.UnMute();
                    break;
                case PhraseFactory.PhraseType.Melody:
                    CurrentComposition.CurrentProgression.MelodyPhrase.UnMute();
                    break;
                default:
                    break;
            }
        }

        private void SwapAll()
        {
            //All of the other phrases of this type that appear in instances of this progression throughout the song (other than the current one) need to be swapped out
            List<ConcreteProgression> progressions = CurrentComposition.Progressions.Where(pr => pr.ProgressionID == CurrentComposition.CurrentProgression.ProgressionID
                && pr.SequenceID != CurrentComposition.CurrentProgression.SequenceID).ToList();

            //TODO: Fix - currently just recopies the first swapped phrase on top of itself througout the song
            foreach (ConcreteProgression p in progressions)
            {   
                switch (m_pendingPhraseType)
                {
                    case PhraseFactory.PhraseType.Drum:
                        p.Notes.RemoveAll(n => n.Type == Note.NoteType.Drum);
                        p.DrumPhrase = (DrumPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Drum, p, CurrentComposition.CurrentProgression.DrumPhrase);
                        break;
                    case PhraseFactory.PhraseType.Bass:
                        p.Notes.RemoveAll(n => n.Type == Note.NoteType.Bass);
                        p.BassPhrase = (BassPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Bass, p, CurrentComposition.CurrentProgression.BassPhrase);
                        break;
                    case PhraseFactory.PhraseType.Rhythm:
                        p.Notes.RemoveAll(n => n.Type == Note.NoteType.Rhythm);
                        p.RhythmPhrase = (RhythmPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Rhythm, p, CurrentComposition.CurrentProgression.RhythmPhrase);
                        break;
                    case PhraseFactory.PhraseType.Melody:
                        p.Notes.RemoveAll(n => n.Type == Note.NoteType.Lead);
                        p.MelodyPhrase = (MelodyPhrase)m_phraseFactory.GetPhrase(PhraseFactory.PhraseType.Melody, p, CurrentComposition.CurrentProgression.MelodyPhrase);
                        break;
                }
            }
        }

        /// <summary>
        /// Push all the panic buttons
        /// </summary>
        public static void Kill()
        {
            m_processor.Process(new VstEventCollection { ConcreteNote.MelodyOffEvent , ConcreteNote.BassOffEvent, ConcreteNote.RhythmOffEvent});
        }
        #endregion
    }
}
